package cn.stylefeng.roses.kernel.saas.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.auth.api.AuthServiceApi;
import cn.stylefeng.roses.kernel.auth.api.password.PasswordStoredEncryptApi;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
import cn.stylefeng.roses.kernel.config.modular.entity.SysConfig;
import cn.stylefeng.roses.kernel.config.modular.service.SysConfigService;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.druid.DruidProperties;
import cn.stylefeng.roses.kernel.db.api.pojo.entity.BaseEntity;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.db.api.util.DatabaseUtil;
import cn.stylefeng.roses.kernel.dsctn.api.DataSourceApi;
import cn.stylefeng.roses.kernel.dsctn.api.constants.DatasourceContainerConstants;
import cn.stylefeng.roses.kernel.file.modular.entity.SysFileInfo;
import cn.stylefeng.roses.kernel.file.modular.service.SysFileInfoService;
import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
import cn.stylefeng.roses.kernel.rule.enums.StatusEnum;
import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum;
import cn.stylefeng.roses.kernel.saas.api.SaasTenantApi;
import cn.stylefeng.roses.kernel.saas.api.constants.SaasConstants;
import cn.stylefeng.roses.kernel.saas.api.exception.SaasException;
import cn.stylefeng.roses.kernel.saas.api.exception.enums.SaasExceptionEnum;
import cn.stylefeng.roses.kernel.saas.api.util.SaasCodeUtil;
import cn.stylefeng.roses.kernel.saas.entity.SysTenant;
import cn.stylefeng.roses.kernel.saas.factory.TenantUserFactory;
import cn.stylefeng.roses.kernel.saas.mapper.TenantMapper;
import cn.stylefeng.roses.kernel.saas.request.SysTenantRequest;
import cn.stylefeng.roses.kernel.saas.service.TenantService;
import cn.stylefeng.roses.kernel.system.api.pojo.user.request.SysUserRequest;
import cn.stylefeng.roses.kernel.system.modular.organization.entity.HrOrganization;
import cn.stylefeng.roses.kernel.system.modular.organization.service.HrOrganizationService;
import cn.stylefeng.roses.kernel.system.modular.user.entity.SysUser;
import cn.stylefeng.roses.kernel.system.modular.user.entity.SysUserOrg;
import cn.stylefeng.roses.kernel.system.modular.user.service.SysUserOrgService;
import cn.stylefeng.roses.kernel.system.modular.user.service.SysUserRoleService;
import cn.stylefeng.roses.kernel.system.modular.user.service.SysUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 租户业务
 *
 * @author fengshuonan
 * @date 2021/5/26 17:55
 */
@Slf4j
@Service
public class TenantServiceImpl extends ServiceImpl<TenantMapper, SysTenant> implements TenantService {

    @Resource
    private PasswordStoredEncryptApi passwordStoredEncryptApi;

    @Resource
    private SaasTenantApi saasTenantApi;

    @Resource
    private AuthServiceApi authServiceApi;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private DruidProperties druidProperties;

    @Resource
    private DataSourceApi dataSourceApi;

    @Resource
    private SysUserRoleService sysUserRoleService;

    @Resource
    private HrOrganizationService hrOrganizationService;

    @Resource
    private SysUserOrgService sysUserOrgService;

    @Resource
    private SysFileInfoService sysFileInfoService;

    @Resource
    private SysConfigService sysConfigService;

    @Override
    public void register(SysTenantRequest sysTenantRequest) {
        // 请求参数转化为实体
        SysTenant sysTenant = new SysTenant();
        BeanUtil.copyProperties(sysTenantRequest, sysTenant);

        // 加密密码，Bcrypt算法
        String encryptPassword = this.passwordStoredEncryptApi.encrypt(sysTenantRequest.getPassword());
        sysTenant.setPassword(encryptPassword);

        // 设置状态未删除,设置启用状态
        sysTenant.setDelFlag(YesOrNotEnum.N.getCode());
        sysTenant.setStatusFlag(StatusEnum.ENABLE.getCode());

        // 获取可分配的租户数据库编码
        String tenantCode = this.calcAssignableTenantCode();
        sysTenant.setTenantCode(tenantCode);

        // 插入到数据库
        this.save(sysTenant);

        // 切换租户数据源
        changeTenantDataSource(sysTenant, tenantCode);

        // 创建租户库的超级管理员账号，账号就是公司的注册手机
        SysUser sysUser = createTenantSuperAdminAccount(sysTenant);

        // 创建一个机构，名称为注册的租户名称
        createTenantCompany(sysTenant, sysUser.getUserId());
    }

    @Override
    public void edit(SysTenantRequest sysTenantRequest) {
        SysTenant sysTenant = this.queryTenant(sysTenantRequest);
        BeanUtil.copyProperties(sysTenantRequest, sysTenant);

        // 不能修改状态，租户编码等信息
        sysTenant.setTenantCode(null);
        sysTenant.setDelFlag(null);
        sysTenant.setStatusFlag(null);

        this.updateById(sysTenant);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void del(SysTenantRequest sysTenantRequest) {
        SysTenant sysTenant = this.queryTenant(sysTenantRequest);

        // 删除租户的数据源信息
        String datasourceCode = RuleConstants.TENANT_DB_PREFIX + sysTenant.getTenantCode();
        dataSourceApi.deleteByDatasourceCode(datasourceCode);

        // 删除租户信息
        sysTenant.setDelFlag(YesOrNotEnum.Y.getCode());
        this.updateById(sysTenant);
    }

    @Override
    public PageResult<SysTenant> findPage(SysTenantRequest sysTenantRequest) {
        LambdaQueryWrapper<SysTenant> wrapper = createWrapper(sysTenantRequest);

        wrapper.select(SysTenant::getTenantId,
                SysTenant::getCompanyName,
                SysTenant::getCompanyLogo,
                SysTenant::getCompanyAddress,
                SysTenant::getEmail,
                SysTenant::getSafePhone,
                SysTenant::getStatusFlag,
                SysTenant::getTenantCode,
                BaseEntity::getCreateTime);

        Page<SysTenant> page = this.page(PageFactory.defaultPage(), wrapper);
        return PageResultFactory.createPageResult(page);
    }

    @Override
    public List<SysTenant> getDropDownList() {
        LambdaQueryWrapper<SysTenant> wrapper = this.createWrapper(null);
        wrapper.select(SysTenant::getTenantCode, SysTenant::getCompanyName);
        return this.list(wrapper);
    }

    @Override
    public boolean checkCompanyName(SysTenantRequest sysTenantRequest) {
        String companyName = sysTenantRequest.getCompanyName();
        LambdaQueryWrapper<SysTenant> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysTenant::getDelFlag, YesOrNotEnum.N.getCode());
        wrapper.eq(SysTenant::getCompanyName, companyName);
        return this.count(wrapper) > 0;
    }

    @Override
    public LoginResponse tenantCodeLogin(LoginRequest tenantRequest) {

        // 如果请求租户编码是master，走正常登录
        if (DatasourceContainerConstants.MASTER_DATASOURCE_NAME.equals(tenantRequest.getTenantCode())) {
            tenantRequest.setTenantCode(null);
            return authServiceApi.login(tenantRequest);
        }

        // 查询一下租户存不存在（会抛出异常）
        this.getTenantInfoByTenantCode(tenantRequest.getTenantCode());

        // 切换数据源
        saasTenantApi.changeTenant(tenantRequest.getTenantCode());

        // 根据租户手机号登录
        return authServiceApi.login(tenantRequest);
    }

    @Override
    public SysTenant getTenantInfoByTenantCode(String tenantCode) {
        LambdaQueryWrapper<SysTenant> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysTenant::getTenantCode, tenantCode);
        wrapper.eq(SysTenant::getDelFlag, YesOrNotEnum.N.getCode());
        wrapper.eq(SysTenant::getStatusFlag, StatusEnum.ENABLE.getCode());
        SysTenant sysTenant = this.getOne(wrapper, false);
        if (ObjectUtil.isEmpty(sysTenant)) {
            throw new SaasException(SaasExceptionEnum.TENANT_NOT_EXIST);
        }
        return sysTenant;
    }

    @Override
    public String calcAssignableTenantCode() {
        String tenantCode = SaasCodeUtil.getTenantCode();
        saasTenantApi.initTenantDb(tenantCode, SaasConstants.SAAS_INIT_SQL);
        return tenantCode;
    }

    @Override
    public List<String> calcAssignableTenantCodeArray() {
        List<String> resultTenantCodeList;
        try {
            // 获取数据库连接 查询所有数据库名
            List<String> databasesList = DatabaseUtil.getDatabases(druidProperties);

            // 查询租户预分配的数据库
            List<String> tenantCodeList = new ArrayList<>();
            LambdaQueryWrapper<SysTenant> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(SysTenant::getTenantCode);
            wrapper.eq(SysTenant::getDelFlag, YesOrNotEnum.N.getCode());
            List<SysTenant> sysTenantList = this.list(wrapper);
            if (ArrayUtil.isNotEmpty(sysTenantList)) {
                tenantCodeList = sysTenantList.stream().map(SysTenant::getTenantCode).collect(Collectors.toList());
            }

            // 计算两个集合的差集 取出可以分配给租户的数据库列表
            resultTenantCodeList = CollUtil.subtractToList(databasesList, tenantCodeList);

            // 如果数据库中没有可分配的租户，则临时创建一个租户
            if (resultTenantCodeList.size() <= 0) {
                String tenantCode = SaasCodeUtil.getTenantCode();
                saasTenantApi.initTenantDb(tenantCode, SaasConstants.SAAS_INIT_SQL);

                resultTenantCodeList = new ArrayList<>();
                resultTenantCodeList.add(tenantCode);
            }
            return resultTenantCodeList;
        } catch (Exception e) {
            log.error("获取可分配的租户数据库code错误", e);
            throw new SaasException(SaasExceptionEnum.CALC_TENANT_CODE_ERROR, e.getMessage());
        }
    }

    /**
     * 获取租户信息
     *
     * @author fengshuonan
     * @date 2021/5/27 9:59
     */
    private SysTenant queryTenant(SysTenantRequest sysTenantRequest) {
        SysTenant sysTenant = this.getById(sysTenantRequest.getTenantId());
        if (ObjectUtil.isNull(sysTenant)) {
            throw new SaasException(SaasExceptionEnum.TENANT_NOT_EXIST);
        }
        return sysTenant;
    }

    /**
     * 创建wrapper
     *
     * @author fengshuonan
     * @date 2021/5/27 11:15
     */
    private LambdaQueryWrapper<SysTenant> createWrapper(SysTenantRequest sysTenantRequest) {
        LambdaQueryWrapper<SysTenant> queryWrapper = new LambdaQueryWrapper<>();

        // 查询未删除状态的
        queryWrapper.eq(SysTenant::getDelFlag, YesOrNotEnum.N.getCode());

        if (ObjectUtil.isEmpty(sysTenantRequest)) {
            return queryWrapper;
        }

        // 拼接公司名称查询条件
        queryWrapper.like(ObjectUtil.isNotEmpty(sysTenantRequest.getCompanyName()), SysTenant::getCompanyName, sysTenantRequest.getCompanyName());

        // 拼接邮箱查询条件
        queryWrapper.like(ObjectUtil.isNotEmpty(sysTenantRequest.getEmail()), SysTenant::getEmail, sysTenantRequest.getEmail());

        // 拼接手机号查询条件
        queryWrapper.like(ObjectUtil.isNotEmpty(sysTenantRequest.getSafePhone()), SysTenant::getSafePhone, sysTenantRequest.getSafePhone());

        return queryWrapper;
    }

    /**
     * 创建租户的公司
     *
     * @author fengshuonan
     * @date 2021/8/7 9:02
     */
    private void createTenantCompany(SysTenant sysTenant, Long userId) {
        HrOrganization hrOrganization = new HrOrganization();
        hrOrganization.setOrgName(sysTenant.getCompanyName());
        hrOrganization.setOrgParentId(-1L);
        hrOrganization.setOrgPids("[-1],");
        hrOrganization.setOrgCode(sysTenant.getTenantCode());
        hrOrganization.setOrgSort(new BigDecimal(1));
        hrOrganization.setStatusFlag(StatusEnum.ENABLE.getCode());
        hrOrganization.setDelFlag(YesOrNotEnum.N.getCode());
        hrOrganizationService.save(hrOrganization);

        // 用户绑定机构，职务是总经理职务
        SysUserOrg entity = new SysUserOrg();
        entity.setUserId(userId);
        entity.setOrgId(hrOrganization.getOrgId());
        entity.setPositionId(1339554696976781333L);
        sysUserOrgService.save(entity);
    }

    /**
     * 切换租户数据源的操作
     *
     * @author fengshuonan
     * @date 2021/8/7 10:36
     */
    private void changeTenantDataSource(SysTenant sysTenant, String tenantCode) {
        // 切换数据源之前，获取一下公司logo的文件信息
        SysFileInfo fileInfo = null;
        if (ObjectUtil.isNotEmpty(sysTenant.getCompanyLogo())) {
            fileInfo = sysFileInfoService.getById(sysTenant.getCompanyLogo());
        }

        // 切换之前，准备sys_config表中的所有数据，准备copy到租户库
        List<SysConfig> list = sysConfigService.list();

        // 切换到租户的数据库连接池
        saasTenantApi.changeTenant(tenantCode);

        // 租户的图片插入到租户库
        if (fileInfo != null) {
            sysFileInfoService.save(fileInfo);
        }

        // 批量插入配置信息到新库
        sysConfigService.saveBatch(list, list.size());
    }

    /**
     * 创建租户库的超级管理员账号，账号就是公司的注册手机
     *
     * @author fengshuonan
     * @date 2021/8/7 10:37
     */
    private SysUser createTenantSuperAdminAccount(SysTenant sysTenant) {
        SysUser sysUser = TenantUserFactory.createSysUser(sysTenant);
        this.sysUserService.save(sysUser);

        // 为用户绑定超级管理员角色
        SysUserRequest sysUserRequest = new SysUserRequest();
        sysUserRequest.setUserId(sysUser.getUserId());

        // 绑定超级管理员角色，角色id是管理员的角色
        sysUserRequest.setGrantRoleIdList(ListUtil.toList(1339550467939639303L));
        sysUserRoleService.assignRoles(sysUserRequest);
        return sysUser;
    }

}
